﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using MBWebService.Core;
using MBWebService.Core.Enums;
using MBWebService.Core.Classes;
using MediaBrowser;
using MediaBrowser.Library;
using MediaBrowser.Library.Entities;
using MediaBrowser.Library.RemoteControl;
using Microsoft.MediaCenter;

namespace MBWebService.Core.Classes
{
    public class WebServicePlaybackController : PlaybackController
    {
        #region Private Members

        private static NowPlayingItem _nowPlaying = new NowPlayingItem();
        private static readonly object _nowPlayingLockObj = new object();

        private static Dictionary<string, Guid> _dirCache;
        private static readonly object _dirCacheLockObj = new object();

        private static Dictionary<string, Guid> DirCache
        {
            get
            {
                InitDirCache();
                return _dirCache;
            }
        }

        private static void InitDirCache()
        {
            if (_dirCache == null)
            {
                lock (_dirCacheLockObj)
                {
                    if (_dirCache == null)
                    {
                        _dirCache = new Dictionary<string, Guid>();
                        var folder = Kernel.Instance.RootFolder;
                        var item = ItemCacheRecursive(folder);
                    }
                }
            }
        }

        [ThreadStatic]
        private static bool updated = false;

        #endregion

        public WebServicePlaybackController()
        {
            // build the dir cache on a separate thread for speeeeeed.
            ThreadPool.QueueUserWorkItem(_ => { InitDirCache(); });
            OnProgress += new EventHandler<PlaybackStateEventArgs>(WebServicePlaybackController_OnProgress);
        }

        void WebServicePlaybackController_OnProgress(object sender, MediaBrowser.Library.RemoteControl.PlaybackStateEventArgs e)
        {
            Console.WriteLine("{0} - {1}", e.Title, e.Position);
            updated = true;
        }

        public NowPlayingItem GetNowPlaying(bool waitForUpdate)
        {
            NowPlayingItem item = null;
            if (waitForUpdate)
            {
                // Timeout after 20 seconds...
                DateTime now = DateTime.Now;
                // ensure this is the on progress handler...
                updated = false;
                ReAttach();
                while (!updated && 
                    DateTime.Now - now < TimeSpan.FromSeconds(20))
                {
                    Thread.Sleep(10);
                }
                updated = false;
                item = GetNowPlayingItem();
            }
            else
            {
                item = GetNowPlayingItem();
            }

            return item;
        }

        public NowPlayingItem GetNowPlayingItem()
        {
            NowPlayingItem retVal = null;
            MediaTransport transport = MediaTransport;
            Microsoft.MediaCenter.PlayState state = Microsoft.MediaCenter.PlayState.Undefined;
            if (transport != null)
            {
                state = transport.PlayState;
                long duration = transport.Position.Ticks;
                string title = GetMetadata<string>(MediaMetadataTypes.Title);
                
                lock (_nowPlayingLockObj)
                {
                    if (!title.IsNullOrEmpty() && (title != _nowPlaying.Title || _nowPlaying.ElapsedTime.Ticks != duration))
                    {
                        // It's different, so update it...
                        if (title != _nowPlaying.Name)
                        {
                            _nowPlaying = new NowPlayingItem();
                            _nowPlaying.Name = title;
                            _nowPlaying.Title = title;
                            string fileLocation = GetMetadata<string>(MediaMetadataTypes.Uri);
                            if (!fileLocation.IsNullOrEmpty())
                            {
                                fileLocation = fileLocation.Replace(@"file:///", string.Empty);
                                if (fileLocation.Contains(".wpl"))
                                {
                                    fileLocation = GetFileLocationFromWpl(fileLocation, title);
                                }

                                // Replace uri seperator with path seperator...
                                fileLocation = fileLocation.Replace(@"/", @"\");

                                if (DirCache.ContainsKey(fileLocation))
                                {
                                    var item = Kernel.Instance.ItemRepository.RetrieveItem(DirCache[fileLocation]);
                                    _nowPlaying.Id = item.Id;
                                    _nowPlaying.Name = item.Name;
                                }
                            }

                            TimeSpan totalDuration = GetMetadata<TimeSpan>(MediaMetadataTypes.Duration);
                            if (totalDuration.Seconds > 0)
                            {
                                _nowPlaying.Total = totalDuration;
                            }
                        }
                        _nowPlaying.ElapsedTime = TimeSpan.FromTicks(duration);
                    }

                    _nowPlaying.PlayState = (int)state;
                }
                retVal = _nowPlaying;
            }

            return retVal;
        }

        private static string GetFileLocationFromWpl(string fileLocation, string title)
        {
            string retVal = null;
            System.IO.FileInfo file = new System.IO.FileInfo(fileLocation);

            if (File.Exists(file.FullName))
            {
                XDocument doc = XDocument.Load(file.FullName);
                try
                {
                    var elements = doc.Descendants("media").Where(o => o.Attribute("src").Value.Contains(title));
                    foreach (var element in elements)
                    {
                        string path = element.Attribute("src").Value;
                        if (Path.GetFileNameWithoutExtension(path) == title)
                        {
                            retVal = path;
                        }
                    }
                }
                catch { } // die silently...
            }

            return retVal;
        }

        private T GetMetadata<T>(MediaMetadataTypes metadataType)
        {
            T retval = default(T);

            try
            {
                object data = MediaExperience.MediaMetadata[metadataType.ToString()];
                if (typeof(T) == typeof(TimeSpan))
                {
                    object tempData = TimeSpan.Parse(data.ToString());
                    retval = (T)tempData;
                }
                else
                {
                    retval = (T)data;
                }
            }
            catch (Exception e)
            {
                MBWebService.Core.Logging.Logger.Instance.LogMessage("Error getting metadata value {0}; ERROR: {1}", metadataType, e.Message);
            }

            return retval;
        }

        private  static BaseItem ItemCacheRecursive(Folder folder)
        {
            BaseItem retVal = null;

            if (folder != null)
            {
                foreach (var child in folder.Children)
                {
                    if (child is Media )
                    {
                        Media bob = child as Media;
                        foreach (var file in bob.Files)
                        {
                            //Shortcuts contain the same GUID so this will ignore dupes
                            if (_dirCache.ContainsKey(file) == false)
                            {
                                _dirCache.Add(file, bob.Id);
                            }
                        }
                    }

                    if (child is Folder)
                    {

                        retVal = ItemCacheRecursive(child as Folder);
                            if (retVal != null)
                            {
                                break;
                            }
                    }
                }
            }

            return retVal;
        }
    }
}
